/**
 * Copyright (c) 2015, 2016 itemis AG (http://www.itemis.eu) and others.
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License 2.0 which is available at
 * http://www.eclipse.org/legal/epl-2.0.
 * 
 * SPDX-License-Identifier: EPL-2.0
 */
package org.eclipse.xtext.xtext.wizard.ecore2xtext;

import com.google.common.base.Objects;
import com.google.common.collect.Iterables;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.eclipse.emf.ecore.EAttribute;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.ecore.EDataType;
import org.eclipse.emf.ecore.EEnum;
import org.eclipse.emf.ecore.EEnumLiteral;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.xtend2.lib.StringConcatenation;
import org.eclipse.xtext.xbase.lib.CollectionLiterals;
import org.eclipse.xtext.xbase.lib.Functions.Function1;
import org.eclipse.xtext.xbase.lib.IterableExtensions;
import org.eclipse.xtext.xbase.lib.ListExtensions;
import org.eclipse.xtext.xtext.wizard.Ecore2XtextConfiguration;
import org.eclipse.xtext.xtext.wizard.WizardConfiguration;

@SuppressWarnings("all")
public class Ecore2XtextGrammarCreator {
  public CharSequence grammar(final WizardConfiguration config) {
    CharSequence _xblockexpression = null;
    {
      final Ecore2XtextConfiguration it = config.getEcore2Xtext();
      UniqueNameUtil.clearUniqueNames(it.getDefaultEPackageInfo());
      StringConcatenation _builder = new StringConcatenation();
      _builder.append("// automatically generated by Xtext");
      _builder.newLine();
      _builder.append("grammar ");
      String _name = config.getLanguage().getName();
      _builder.append(_name);
      _builder.append(" with org.eclipse.xtext.common.Terminals");
      _builder.newLineIfNotEmpty();
      _builder.newLine();
      {
        Collection<EPackage> _allReferencedEPackages = Ecore2XtextExtensions.allReferencedEPackages(it);
        for(final EPackage it_1 : _allReferencedEPackages) {
          _builder.append("import \"");
          String _nsURI = it_1.getNsURI();
          _builder.append(_nsURI);
          _builder.append("\" ");
          {
            if (((UniqueNameUtil.uniqueName(it_1) != null) && (!Objects.equal(UniqueNameUtil.uniqueName(it_1), "")))) {
              _builder.append("as ");
              String _uniqueName = UniqueNameUtil.uniqueName(it_1);
              _builder.append(_uniqueName);
            }
          }
          _builder.newLineIfNotEmpty();
        }
      }
      _builder.newLine();
      CharSequence _rules = this.rules(it.getRootElementClass());
      _builder.append(_rules);
      _builder.newLineIfNotEmpty();
      {
        List<EClass> _but = this.<EClass>but(Ecore2XtextExtensions.allDispatcherRuleClasses(it), it.getRootElementClass());
        for(final EClass it_2 : _but) {
          _builder.newLine();
          CharSequence _subClassDispatcherRule = this.subClassDispatcherRule(it_2);
          _builder.append(_subClassDispatcherRule);
          _builder.newLineIfNotEmpty();
        }
      }
      {
        List<EClassifier> _but_1 = this.<EClassifier>but(Ecore2XtextExtensions.allConcreteRuleClassifiers(it), it.getRootElementClass());
        for(final EClassifier it_3 : _but_1) {
          _builder.newLine();
          CharSequence _rule = this.rule(it_3);
          _builder.append(_rule);
          _builder.newLineIfNotEmpty();
        }
      }
      _xblockexpression = _builder;
    }
    return _xblockexpression;
  }

  public <T extends EClassifier> List<T> but(final Iterable<T> classes, final EClassifier it) {
    final List<T> retVal = IterableExtensions.<T>toList(classes);
    retVal.remove(it);
    return retVal;
  }

  public CharSequence subClassDispatcherRule(final EClass it) {
    StringConcatenation _builder = new StringConcatenation();
    {
      boolean _needsDispatcherRule = Ecore2XtextExtensions.needsDispatcherRule(it);
      if (_needsDispatcherRule) {
        String _uniqueName = UniqueNameUtil.uniqueName(it);
        _builder.append(_uniqueName);
        _builder.append(" returns ");
        String _fqn = Ecore2XtextExtensions.fqn(it);
        _builder.append(_fqn);
        _builder.append(":");
        _builder.newLineIfNotEmpty();
        _builder.append("\t");
        String _subClassAlternatives = this.subClassAlternatives(it);
        _builder.append(_subClassAlternatives, "\t");
        _builder.append(";");
        _builder.newLineIfNotEmpty();
      }
    }
    return _builder;
  }

  public String subClassAlternatives(final EClass eClazz) {
    String _xblockexpression = null;
    {
      ArrayList<EClass> _newArrayList = CollectionLiterals.<EClass>newArrayList(eClazz);
      Iterable<EClass> _subClasses = Ecore2XtextExtensions.subClasses(eClazz);
      Iterable<EClass> list = Iterables.<EClass>concat(_newArrayList, _subClasses);
      final Function1<EClass, Boolean> _function = (EClass c) -> {
        return Boolean.valueOf(Ecore2XtextExtensions.needsConcreteRule(c));
      };
      list = IterableExtensions.<EClass>filter(list, _function);
      final Function1<EClass, String> _function_1 = (EClass it) -> {
        return Ecore2XtextExtensions.concreteRuleName(it);
      };
      _xblockexpression = IterableExtensions.join(IterableExtensions.<EClass, String>map(list, _function_1), " | ");
    }
    return _xblockexpression;
  }

  public CharSequence idAssignment(final EClass it) {
    CharSequence _xblockexpression = null;
    {
      final EAttribute idAttr = Ecore2XtextExtensions.idAttribute(it);
      CharSequence _xifexpression = null;
      if ((idAttr != null)) {
        StringConcatenation _builder = new StringConcatenation();
        String _name = idAttr.getName();
        _builder.append(_name);
        _builder.append("=");
        String _assignedRuleCall = Ecore2XtextExtensions.assignedRuleCall(idAttr);
        _builder.append(_assignedRuleCall);
        _xifexpression = _builder;
      }
      _xblockexpression = _xifexpression;
    }
    return _xblockexpression;
  }

  public CharSequence assigment(final EStructuralFeature it) {
    StringConcatenation _builder = new StringConcatenation();
    {
      boolean _isRequired = it.isRequired();
      boolean _not = (!_isRequired);
      if (_not) {
        _builder.append("(");
      }
    }
    String _assignmentKeyword = Ecore2XtextExtensions.assignmentKeyword(it);
    _builder.append(_assignmentKeyword);
    {
      boolean _isMany = it.isMany();
      if (_isMany) {
        {
          boolean _isContainment = Ecore2XtextExtensions.isContainment(it);
          if (_isContainment) {
            _builder.append("\'{\' ");
          } else {
            _builder.append("\'(\' ");
          }
        }
      }
    }
    String _quoteIfNeccesary = Ecore2XtextExtensions.quoteIfNeccesary(it.getName());
    _builder.append(_quoteIfNeccesary);
    CharSequence _assignmentOperator = this.assignmentOperator(it);
    _builder.append(_assignmentOperator);
    CharSequence _assignedTerminal = this.assignedTerminal(it);
    _builder.append(_assignedTerminal);
    {
      boolean _isMany_1 = it.isMany();
      if (_isMany_1) {
        _builder.append(" ( \",\" ");
        String _quoteIfNeccesary_1 = Ecore2XtextExtensions.quoteIfNeccesary(it.getName());
        _builder.append(_quoteIfNeccesary_1);
        CharSequence _assignmentOperator_1 = this.assignmentOperator(it);
        _builder.append(_assignmentOperator_1);
        CharSequence _assignedTerminal_1 = this.assignedTerminal(it);
        _builder.append(_assignedTerminal_1);
        _builder.append(")* ");
        {
          boolean _isContainment_1 = Ecore2XtextExtensions.isContainment(it);
          if (_isContainment_1) {
            _builder.append("\'}\' ");
          } else {
            _builder.append("\')\' ");
          }
        }
      }
    }
    {
      boolean _isRequired_1 = it.isRequired();
      boolean _not_1 = (!_isRequired_1);
      if (_not_1) {
        _builder.append(")?");
      }
    }
    _builder.newLineIfNotEmpty();
    return _builder;
  }

  public CharSequence assignedTerminal(final EStructuralFeature it) {
    CharSequence _switchResult = null;
    boolean _matched = false;
    if (it instanceof EAttribute) {
      _matched=true;
      _switchResult = Ecore2XtextExtensions.assignedRuleCall(((EAttribute)it));
    }
    if (!_matched) {
      if (it instanceof EReference) {
        _matched=true;
        CharSequence _xifexpression = null;
        boolean _isContainment = ((EReference)it).isContainment();
        if (_isContainment) {
          _xifexpression = UniqueNameUtil.uniqueName(((EReference)it).getEReferenceType());
        } else {
          StringConcatenation _builder = new StringConcatenation();
          _builder.append("[");
          String _fqn = Ecore2XtextExtensions.fqn(((EReference)it).getEReferenceType());
          _builder.append(_fqn);
          _builder.append("|EString]");
          _xifexpression = _builder;
        }
        _switchResult = _xifexpression;
      }
    }
    if (!_matched) {
      StringConcatenation _builder = new StringConcatenation();
      _switchResult = _builder;
    }
    return _switchResult;
  }

  public CharSequence assignmentOperator(final EStructuralFeature it) {
    StringConcatenation _builder = new StringConcatenation();
    {
      boolean _isMany = it.isMany();
      if (_isMany) {
        _builder.append("+=");
      } else {
        if ((Ecore2XtextExtensions.isBoolean(it.getEType()) && Ecore2XtextExtensions.isPrefixBooleanFeature(it))) {
          _builder.append("?=");
        } else {
          _builder.append("=");
        }
      }
    }
    return _builder;
  }

  public CharSequence rules(final EClassifier it) {
    CharSequence _xifexpression = null;
    if (((it != null) && Ecore2XtextExtensions.needsConcreteRule(it))) {
      _xifexpression = this.rule(it);
    }
    return _xifexpression;
  }

  public CharSequence rule(final EClassifier it) {
    CharSequence _switchResult = null;
    boolean _matched = false;
    if (it instanceof EClass) {
      _matched=true;
      StringConcatenation _builder = new StringConcatenation();
      String _concreteRuleName = Ecore2XtextExtensions.concreteRuleName(((EClass)it));
      _builder.append(_concreteRuleName);
      _builder.append(" returns ");
      String _fqn = Ecore2XtextExtensions.fqn(it);
      _builder.append(_fqn);
      _builder.append(":");
      _builder.newLineIfNotEmpty();
      {
        boolean _onlyOptionalFeatures = Ecore2XtextExtensions.onlyOptionalFeatures(((EClass)it));
        if (_onlyOptionalFeatures) {
          _builder.append("\t");
          _builder.append("{");
          String _fqn_1 = Ecore2XtextExtensions.fqn(it);
          _builder.append(_fqn_1, "\t");
          _builder.append("}");
          _builder.newLineIfNotEmpty();
        }
      }
      {
        Iterable<EStructuralFeature> _prefixFeatures = Ecore2XtextExtensions.prefixFeatures(((EClass)it));
        for(final EStructuralFeature strF : _prefixFeatures) {
          _builder.append("\t");
          CharSequence _assigment = this.assigment(strF);
          _builder.append(_assigment, "\t");
          _builder.newLineIfNotEmpty();
        }
      }
      _builder.append("\t");
      _builder.append("\'");
      String _name = ((EClass)it).getName();
      _builder.append(_name, "\t");
      _builder.append("\'");
      _builder.newLineIfNotEmpty();
      _builder.append("\t");
      CharSequence _idAssignment = this.idAssignment(((EClass)it));
      _builder.append(_idAssignment, "\t");
      {
        boolean _isEmpty = IterableExtensions.isEmpty(Ecore2XtextExtensions.inlinedFeatures(((EClass)it)));
        boolean _not = (!_isEmpty);
        if (_not) {
          _builder.newLineIfNotEmpty();
          _builder.append("\t");
          _builder.append("\'{\'");
          _builder.newLine();
          {
            Iterable<EAttribute> _allAttributes = Ecore2XtextExtensions.allAttributes(((EClass)it));
            for(final EAttribute attr : _allAttributes) {
              _builder.append("\t");
              _builder.append("\t");
              CharSequence _assigment_1 = this.assigment(attr);
              _builder.append(_assigment_1, "\t\t");
              _builder.newLineIfNotEmpty();
            }
          }
          {
            Iterable<EReference> _allCrossReferences = Ecore2XtextExtensions.allCrossReferences(((EClass)it));
            for(final EReference ref : _allCrossReferences) {
              _builder.append("\t");
              _builder.append("\t");
              CharSequence _assigment_2 = this.assigment(ref);
              _builder.append(_assigment_2, "\t\t");
              _builder.newLineIfNotEmpty();
            }
          }
          {
            Iterable<EReference> _allContainmentReferences = Ecore2XtextExtensions.allContainmentReferences(((EClass)it));
            for(final EReference conti : _allContainmentReferences) {
              _builder.append("\t");
              _builder.append("\t");
              CharSequence _assigment_3 = this.assigment(conti);
              _builder.append(_assigment_3, "\t\t");
              _builder.newLineIfNotEmpty();
            }
          }
          _builder.append("\t");
          _builder.append("\'}\'");
        }
      }
      _builder.append(";");
      _builder.newLineIfNotEmpty();
      _switchResult = _builder;
    }
    if (!_matched) {
      if (it instanceof EEnum) {
        _matched=true;
        StringConcatenation _builder = new StringConcatenation();
        _builder.append("enum ");
        String _quoteIfNeccesary = Ecore2XtextExtensions.quoteIfNeccesary(((EEnum)it).getName());
        _builder.append(_quoteIfNeccesary);
        _builder.append(" returns ");
        String _fqn = Ecore2XtextExtensions.fqn(it);
        _builder.append(_fqn);
        _builder.append(":");
        _builder.newLineIfNotEmpty();
        _builder.append("\t\t\t\t");
        final Function1<EEnumLiteral, String> _function = (EEnumLiteral it_1) -> {
          String _quoteIfNeccesary_1 = Ecore2XtextExtensions.quoteIfNeccesary(it_1.getName());
          String _plus = (_quoteIfNeccesary_1 + " = \'");
          String _name = it_1.getName();
          String _plus_1 = (_plus + _name);
          return (_plus_1 + "\'");
        };
        String _join = IterableExtensions.join(ListExtensions.<EEnumLiteral, String>map(((EEnum)it).getELiterals(), _function), " | ");
        _builder.append(_join, "\t\t\t\t");
        _builder.append(";");
        _switchResult = _builder;
      }
    }
    if (!_matched) {
      if (it instanceof EDataType) {
        _matched=true;
        CharSequence _xifexpression = null;
        boolean _isSerializable = ((EDataType)it).isSerializable();
        if (_isSerializable) {
          StringConcatenation _builder = new StringConcatenation();
          String _uniqueName = UniqueNameUtil.uniqueName(it);
          _builder.append(_uniqueName);
          _builder.append(" returns ");
          String _fqn = Ecore2XtextExtensions.fqn(it);
          _builder.append(_fqn);
          _builder.append(":");
          _builder.newLineIfNotEmpty();
          _builder.append("\t");
          String _dataTypeRuleBody = Ecore2XtextExtensions.dataTypeRuleBody(((EDataType)it));
          _builder.append(_dataTypeRuleBody, "\t");
          _builder.append(";");
          _builder.newLineIfNotEmpty();
          _xifexpression = _builder;
        }
        _switchResult = _xifexpression;
      }
    }
    if (!_matched) {
      throw new IllegalStateException(("No rule template for " + it));
    }
    return _switchResult;
  }
}
